/*-------------------------------------------------------------------------
 *
 * varchar.c
 *	  内置类型 char(n) 和 varchar(n) 的函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/varchar.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/detoast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "common/hashfn.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "nodes/nodeFuncs.h"
#include "nodes/supportnodes.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/pg_locale.h"
#include "utils/varlena.h"

/* bpchartypmodin 和 varchartypmodin 的公共代码 */
static int32 fc_anychar_typmodin(ArrayType *fc_ta, const char *fc_typename)
{
	int32		fc_typmod;
	int32	   *fc_tl;
	int			fc_n;

	fc_tl = ArrayGetIntegerTypmods(fc_ta, &fc_n);

	/*
	 * 我们对这里好的错误消息没有太大的紧张，因为语法
	 * 不应允许 CHAR 的修饰符数量错误
	 */
	if (fc_n != 1)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid type modifier")));

	if (*fc_tl < 1)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("length for type %s must be at least 1", fc_typename)));
	if (*fc_tl > MaxAttrSize)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("length for type %s cannot exceed %d",
						fc_typename, MaxAttrSize)));

	/*
	 * 出于很大程度上的历史原因，typmod 是 VARHDRSZ 加上字符数量；
	 * 有足够的客户端代码知道这一点，因此我们最好不要改变它。
	 */
	fc_typmod = VARHDRSZ + *fc_tl;

	return fc_typmod;
}

/* bpchartypmodout 和 varchartypmodout 的公共代码 */
static char * fc_anychar_typmodout(int32 fc_typmod)
{
	char	   *fc_res = (char *) palloc(64);

	if (fc_typmod > VARHDRSZ)
		snprintf(fc_res, 64, "(%d)", (int) (fc_typmod - VARHDRSZ));
	else
		*fc_res = '\0';

	return fc_res;
}


/*
 * CHAR() 和 VARCHAR() 类型是 SQL标准的一部分。CHAR()
 * 是用于在 CREATE TABLE 中指定长度的空白填充字符串。
 * VARCHAR 用于存储长度最多为 CREATE TABLE 时指定的长度的字符串。
 *
 * 实现这些类型很难，因为我们无法从类型本身确定类型的长度。
 * 我更改了（希望是所有）调用数据类型输入函数的 fmgr 调用，以提供
 * 长度。（例如，在 INSERT 中，我们有包含属性长度的 tupleDescriptor，
 * 从而得到 char() 或 varchar() 的确切长度。我们将此传递给 bpcharin() 或 varcharin()。）
 * 在无法确定长度的情况下，我们传入 -1，而输入
 * 转换器不会强制任何长度检查。
 *
 * 我们实际上将其实现为 varlena，以便我们不必传入
 * 进行比较函数的长度。（这些类型与“text”之间的区别是
 * 我们在插入时截断并可能填充字符串。）
 *
 *  - ay 6/95
 */


/*****************************************************************************
 *	 bpchar - char()														 *
 *****************************************************************************/

/*
 * bpchar_input -- bpcharin 和 bpcharrecv 的共同部分
 *
 * s 是长度为 len 的输入文本（可能不是以 null 终止）
 * atttypmod 是要应用的 typmod 值
 *
 * 请注意，atttypmod 以字符为单位进行测量，这
 * 不一定与字节数相同。
 *
 * 如果输入字符串过长，抛出错误，除非额外的
 * 字符是空格，在这种情况下它们会被截断。（根据 SQL）
 */
static BpChar *
fc_bpchar_input(const char *fc_s, size_t fc_len, int32 fc_atttypmod)
{
	BpChar	   *fc_result;
	char	   *fc_r;
	size_t		fc_maxlen;

	/* 如果 typmod 是 -1（或无效），则使用实际字符串长度 */
	if (fc_atttypmod < (int32) VARHDRSZ)
		fc_maxlen = fc_len;
	else
	{
		size_t		fc_charlen;	/* 输入中的字符数 */

		fc_maxlen = fc_atttypmod - VARHDRSZ;
		fc_charlen = pg_mbstrlen_with_len(fc_s, fc_len);
		if (fc_charlen > fc_maxlen)
		{
			/* 验证额外字符是否为空格，并去掉它们 */
			size_t		fc_mbmaxlen = pg_mbcharcliplen(fc_s, fc_len, fc_maxlen);
			size_t		fc_j;

			/*
			 * 此时，len是输入字符串的实际字节长度，
			 * maxlen是此bpchar类型允许的最大字符数，
			 * mbmaxlen是这些字符的字节长度。
			 */
			for (fc_j = fc_mbmaxlen; fc_j < fc_len; fc_j++)
			{
				if (fc_s[fc_j] != ' ')
					ereport(ERROR,
							(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
							 errmsg("value too long for type character(%d)",
									(int) fc_maxlen)));
			}

			/*
			 * 现在我们将maxlen设置为必要的字节长度，而不是字符数！
			 */
			fc_maxlen = fc_len = fc_mbmaxlen;
		}
		else
		{
			/*
			 * 现在我们将maxlen设置为必要的字节长度，而不是字符数！
			 */
			fc_maxlen = fc_len + (fc_maxlen - fc_charlen);
		}
	}

	fc_result = (BpChar *) palloc(fc_maxlen + VARHDRSZ);
	SET_VARSIZE(fc_result, fc_maxlen + VARHDRSZ);
	fc_r = VARDATA(fc_result);
	memcpy(fc_r, fc_s, fc_len);

	/* 如有必要，空白填充字符串 */
	if (fc_maxlen > fc_len)
		memset(fc_r + fc_len, ' ', fc_maxlen - fc_len);

	return fc_result;
}

/*
 * 将C字符串转换为字符内部表示。 atttypmod
 * 是类型声明的长度加上VARHDRSZ。
 */
Datum bpcharin(PG_FUNCTION_ARGS)
{
	char	   *fc_s = PG_GETARG_CSTRING(0);

#ifdef NOT_USED
	Oid			fc_typelem = PG_GETARG_OID(1);
#endif
	int32		fc_atttypmod = PG_GETARG_INT32(2);
	BpChar	   *fc_result;

	fc_result = fc_bpchar_input(fc_s, strlen(fc_s), fc_atttypmod);
	PG_RETURN_BPCHAR_P(fc_result);
}


/*
 * 将字符值转换为C字符串。
 *
 * 使用文本转换函数，这只在BpChar和文本是等效类型时适用。
 */
Datum bpcharout(PG_FUNCTION_ARGS)
{
	Datum		fc_txt = PG_GETARG_DATUM(0);

	PG_RETURN_CSTRING(TextDatumGetCString(fc_txt));
}

/*
 *		bpcharrecv			- 将外部二进制格式转换为bpchar
 */
Datum bpcharrecv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

#ifdef NOT_USED
	Oid			fc_typelem = PG_GETARG_OID(1);
#endif
	int32		fc_atttypmod = PG_GETARG_INT32(2);
	BpChar	   *fc_result;
	char	   *fc_str;
	int			fc_nbytes;

	fc_str = pq_getmsgtext(fc_buf, fc_buf->len - fc_buf->cursor, &fc_nbytes);
	fc_result = fc_bpchar_input(fc_str, fc_nbytes, fc_atttypmod);
	pfree(fc_str);
	PG_RETURN_BPCHAR_P(fc_result);
}

/*
 *		bpcharsend			- 将bpchar转换为二进制格式
 */
Datum bpcharsend(PG_FUNCTION_ARGS)
{
	/* 与textsend完全相同，因此共享代码 */
	return textsend(fcinfo);
}


/*
 * 将字符类型转换为指定大小。
 *
 * maxlen是typmod，即声明的长度加上VARHDRSZ字节。
 * isExplicit在进行显式转换为char(N)时为真。
 *
 * 截断规则：对于显式转换，默默截断到给定长度；对于隐式转换，除非额外字符都是空格，否则引发错误。（这在某种程度上是符合SQL的：规范实际上会要求我们为显式转换情况引发“完成条件”，但Postgres没有这样的概念。）
 */
Datum bpchar(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_source = PG_GETARG_BPCHAR_PP(0);
	int32		fc_maxlen = PG_GETARG_INT32(1);
	bool		fc_isExplicit = PG_GETARG_BOOL(2);
	BpChar	   *fc_result;
	int32		fc_len;
	char	   *fc_r;
	char	   *fc_s;
	int			fc_i;
	int			fc_charlen;		/* 输入字符串中的字符数 + 
								 * VARHDRSZ */

	/* 如果typmod无效，则不进行操作 */
	if (fc_maxlen < (int32) VARHDRSZ)
		PG_RETURN_BPCHAR_P(fc_source);

	fc_maxlen -= VARHDRSZ;

	fc_len = VARSIZE_ANY_EXHDR(fc_source);
	fc_s = VARDATA_ANY(fc_source);

	fc_charlen = pg_mbstrlen_with_len(fc_s, fc_len);

	/* 如果提供的数据已经符合typmod，则不进行操作 */
	if (fc_charlen == fc_maxlen)
		PG_RETURN_BPCHAR_P(fc_source);

	if (fc_charlen > fc_maxlen)
	{
		/* 验证额外字符是否为空格，并去掉它们 */
		size_t		fc_maxmblen;

		fc_maxmblen = pg_mbcharcliplen(fc_s, fc_len, fc_maxlen);

		if (!fc_isExplicit)
		{
			for (fc_i = fc_maxmblen; fc_i < fc_len; fc_i++)
				if (fc_s[fc_i] != ' ')
					ereport(ERROR,
							(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
							 errmsg("value too long for type character(%d)",
									fc_maxlen)));
		}

		fc_len = fc_maxmblen;

		/*
		 * 此时，maxlen是必要的字节长度，而不是字符数！
		 */
		fc_maxlen = fc_len;
	}
	else
	{
		/*
		 * 此时，maxlen是必要的字节长度，而不是字符数！
		 */
		fc_maxlen = fc_len + (fc_maxlen - fc_charlen);
	}

	Assert(fc_maxlen >= fc_len);

	fc_result = palloc(fc_maxlen + VARHDRSZ);
	SET_VARSIZE(fc_result, fc_maxlen + VARHDRSZ);
	fc_r = VARDATA(fc_result);

	memcpy(fc_r, fc_s, fc_len);

	/* 如有必要，空白填充字符串 */
	if (fc_maxlen > fc_len)
		memset(fc_r + fc_len, ' ', fc_maxlen - fc_len);

	PG_RETURN_BPCHAR_P(fc_result);
}


/* char_bpchar()
 * 将字符转换为bpchar(1)。
 */
Datum char_bpchar(PG_FUNCTION_ARGS)
{
	char		fc_c = PG_GETARG_CHAR(0);
	BpChar	   *fc_result;

	fc_result = (BpChar *) palloc(VARHDRSZ + 1);

	SET_VARSIZE(fc_result, VARHDRSZ + 1);
	*(VARDATA(fc_result)) = fc_c;

	PG_RETURN_BPCHAR_P(fc_result);
}


/* bpchar_name()
 * 将bpchar()类型转换为NameData类型。
 */
Datum bpchar_name(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_s = PG_GETARG_BPCHAR_PP(0);
	char	   *fc_s_data;
	Name		fc_result;
	int			fc_len;

	fc_len = VARSIZE_ANY_EXHDR(fc_s);
	fc_s_data = VARDATA_ANY(fc_s);

	/* 截断超长输入 */
	if (fc_len >= NAMEDATALEN)
		fc_len = pg_mbcliplen(fc_s_data, fc_len, NAMEDATALEN - 1);

	/* 移除尾部空格 */
	while (fc_len > 0)
	{
		if (fc_s_data[fc_len - 1] != ' ')
			break;
		fc_len--;
	}

	/* 我们在这里使用 palloc0 以确保结果是零填充的 */
	fc_result = (Name) palloc0(NAMEDATALEN);
	memcpy(NameStr(*fc_result), fc_s_data, fc_len);

	PG_RETURN_NAME(fc_result);
}

/* name_bpchar()
 * 将NameData类型转换为bpchar类型。
 *
 * 使用文本转换函数，这只在BpChar和文本是等效类型时适用。
 */
Datum name_bpchar(PG_FUNCTION_ARGS)
{
	Name		fc_s = PG_GETARG_NAME(0);
	BpChar	   *fc_result;

	fc_result = (BpChar *) cstring_to_text(NameStr(*fc_s));
	PG_RETURN_BPCHAR_P(fc_result);
}

Datum bpchartypmodin(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_ta = PG_GETARG_ARRAYTYPE_P(0);

	PG_RETURN_INT32(fc_anychar_typmodin(fc_ta, "char"));
}

Datum bpchartypmodout(PG_FUNCTION_ARGS)
{
	int32		fc_typmod = PG_GETARG_INT32(0);

	PG_RETURN_CSTRING(fc_anychar_typmodout(fc_typmod));
}


/*****************************************************************************
 *	 varchar - varchar(n)
 *
 * 注意：varchar在大多数操作中依赖于文本类型，因此除了I/O和typmod检查外，没有C代码函数。
 *****************************************************************************/

/*
 * varchar_input -- varcharin和varcharrecv的共同内容
 *
 * s是长度为len的输入文本（可能没有以null结尾）
 * atttypmod是要应用的typmod值
 *
 * 注意，atttypmod是以字符为单位测量的，这
 * 并不一定与字节数相同。
 *
 * 如果输入字符串过长，则引发错误，除非额外
 * 字符为空格，在这种情况下它们会被截断。（符合SQL）
 *
 * 使用C字符串到文本的转换函数，只有在VarChar和文本是等效类型时才适用。
 */
static VarChar *
fc_varchar_input(const char *fc_s, size_t fc_len, int32 fc_atttypmod)
{
	VarChar    *fc_result;
	size_t		fc_maxlen;

	fc_maxlen = fc_atttypmod - VARHDRSZ;

	if (fc_atttypmod >= (int32) VARHDRSZ && fc_len > fc_maxlen)
	{
		/* 验证额外字符是否为空格，并去掉它们 */
		size_t		fc_mbmaxlen = pg_mbcharcliplen(fc_s, fc_len, fc_maxlen);
		size_t		fc_j;

		for (fc_j = fc_mbmaxlen; fc_j < fc_len; fc_j++)
		{
			if (fc_s[fc_j] != ' ')
				ereport(ERROR,
						(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
						 errmsg("value too long for type character varying(%d)",
								(int) fc_maxlen)));
		}

		fc_len = fc_mbmaxlen;
	}

	fc_result = (VarChar *) cstring_to_text_with_len(fc_s, fc_len);
	return fc_result;
}

/*
 * 将C字符串转换为VARCHAR内部表示。 atttypmod
 * 是类型声明的长度加上VARHDRSZ。
 */
Datum varcharin(PG_FUNCTION_ARGS)
{
	char	   *fc_s = PG_GETARG_CSTRING(0);

#ifdef NOT_USED
	Oid			fc_typelem = PG_GETARG_OID(1);
#endif
	int32		fc_atttypmod = PG_GETARG_INT32(2);
	VarChar    *fc_result;

	fc_result = fc_varchar_input(fc_s, strlen(fc_s), fc_atttypmod);
	PG_RETURN_VARCHAR_P(fc_result);
}


/*
 * 将VARCHAR值转换为C字符串。
 *
 * 使用文本到C字符串的转换函数，只有在VarChar和文本是等效类型时才适用。
 */
Datum varcharout(PG_FUNCTION_ARGS)
{
	Datum		fc_txt = PG_GETARG_DATUM(0);

	PG_RETURN_CSTRING(TextDatumGetCString(fc_txt));
}

/*
 *		varcharrecv			- 将外部二进制格式转换为varchar
 */
Datum varcharrecv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

#ifdef NOT_USED
	Oid			fc_typelem = PG_GETARG_OID(1);
#endif
	int32		fc_atttypmod = PG_GETARG_INT32(2);
	VarChar    *fc_result;
	char	   *fc_str;
	int			fc_nbytes;

	fc_str = pq_getmsgtext(fc_buf, fc_buf->len - fc_buf->cursor, &fc_nbytes);
	fc_result = fc_varchar_input(fc_str, fc_nbytes, fc_atttypmod);
	pfree(fc_str);
	PG_RETURN_VARCHAR_P(fc_result);
}

/*
 *		varcharsend			- 将varchar转换为二进制格式
 */
Datum varcharsend(PG_FUNCTION_ARGS)
{
	/* 与textsend完全相同，因此共享代码 */
	return textsend(fcinfo);
}


/*
 * varchar_support()
 *
 * varchar()长度强制函数的计划支持函数。
 *
 * 目前，我们唯一能做的有趣事情是平展设置
 * 新最大长度>=以前最大长度的调用。我们可以忽略
 * isExplicit参数，因为它只影响截断情况。
 */
Datum varchar_support(PG_FUNCTION_ARGS)
{
	Node	   *fc_rawreq = (Node *) PG_GETARG_POINTER(0);
	Node	   *fc_ret = NULL;

	if (IsA(fc_rawreq, SupportRequestSimplify))
	{
		SupportRequestSimplify *fc_req = (SupportRequestSimplify *) fc_rawreq;
		FuncExpr   *fc_expr = fc_req->fcall;
		Node	   *fc_typmod;

		Assert(list_length(fc_expr->args) >= 2);

		fc_typmod = (Node *) lsecond(fc_expr->args);

		if (IsA(fc_typmod, Const) && !((Const *) fc_typmod)->constisnull)
		{
			Node	   *fc_source = (Node *) linitial(fc_expr->args);
			int32		fc_old_typmod = exprTypmod(fc_source);
			int32		fc_new_typmod = DatumGetInt32(((Const *) fc_typmod)->constvalue);
			int32		fc_old_max = fc_old_typmod - VARHDRSZ;
			int32		fc_new_max = fc_new_typmod - VARHDRSZ;

			if (fc_new_typmod < 0 || (fc_old_typmod >= 0 && fc_old_max <= fc_new_max))
				fc_ret = relabel_to_typmod(fc_source, fc_new_typmod);
		}
	}

	PG_RETURN_POINTER(fc_ret);
}

/*
 * 将VARCHAR类型转换为指定大小。
 *
 * maxlen是typmod，即声明的长度加上VARHDRSZ字节。
 * isExplicit在进行显式转换为varchar(N)时为真。
 *
 * 截断规则：对于显式转换，默默截断到给定
 * 长度；对于隐式转换，除非额外字符都是空格，否则引发错误。（这在某种程度上是符合SQL的：规范实际上会要求我们为显式转换情况引发“完成条件”，但Postgres没有这样的概念。）
 */
Datum varchar(PG_FUNCTION_ARGS)
{
	VarChar    *fc_source = PG_GETARG_VARCHAR_PP(0);
	int32		fc_typmod = PG_GETARG_INT32(1);
	bool		fc_isExplicit = PG_GETARG_BOOL(2);
	int32		fc_len,
				fc_maxlen;
	size_t		fc_maxmblen;
	int			fc_i;
	char	   *fc_s_data;

	fc_len = VARSIZE_ANY_EXHDR(fc_source);
	fc_s_data = VARDATA_ANY(fc_source);
	fc_maxlen = fc_typmod - VARHDRSZ;

	/* 如果typmod无效或提供的数据已经符合，则不进行操作 */
	if (fc_maxlen < 0 || fc_len <= fc_maxlen)
		PG_RETURN_VARCHAR_P(fc_source);

	/* 只有在字符串过长时才会到这里... */

	/* 截断多字节字符串以保持多字节边界 */
	fc_maxmblen = pg_mbcharcliplen(fc_s_data, fc_len, fc_maxlen);

	if (!fc_isExplicit)
	{
		for (fc_i = fc_maxmblen; fc_i < fc_len; fc_i++)
			if (fc_s_data[fc_i] != ' ')
				ereport(ERROR,
						(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
						 errmsg("value too long for type character varying(%d)",
								fc_maxlen)));
	}

	PG_RETURN_VARCHAR_P((VarChar *) cstring_to_text_with_len(fc_s_data,
															 fc_maxmblen));
}

Datum varchartypmodin(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_ta = PG_GETARG_ARRAYTYPE_P(0);

	PG_RETURN_INT32(fc_anychar_typmodin(fc_ta, "varchar"));
}

Datum varchartypmodout(PG_FUNCTION_ARGS)
{
	int32		fc_typmod = PG_GETARG_INT32(0);

	PG_RETURN_CSTRING(fc_anychar_typmodout(fc_typmod));
}


/*****************************************************************************
 * 导出的函数
 *****************************************************************************/

/* "真实"长度（不计算尾随空格）一个 BpChar */
static inline int fc_bcTruelen(BpChar *fc_arg)
{
	return bpchartruelen(VARDATA_ANY(fc_arg), VARSIZE_ANY_EXHDR(fc_arg));
}

int bpchartruelen(char *fc_s, int fc_len)
{
	int			fc_i;

	/* 注意我们依靠假设 ' ' 在每个支持的多字节服务器编码上都是单例单位。 */
	for (fc_i = fc_len - 1; fc_i >= 0; fc_i--)
	{
		if (fc_s[fc_i] != ' ')
			break;
	}
	return fc_i + 1;
}

Datum bpcharlen(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg = PG_GETARG_BPCHAR_PP(0);
	int			fc_len;

	/* 获取字节数，忽略尾随空格 */
	fc_len = fc_bcTruelen(fc_arg);

	/* 在多字节编码中，转换为字符数量 */
	if (pg_database_encoding_max_length() != 1)
		fc_len = pg_mbstrlen_with_len(VARDATA_ANY(fc_arg), fc_len);

	PG_RETURN_INT32(fc_len);
}

Datum bpcharoctetlen(PG_FUNCTION_ARGS)
{
	Datum		fc_arg = PG_GETARG_DATUM(0);

	/* 我们根本无需解压输入 */
	PG_RETURN_INT32(toast_raw_datum_size(fc_arg) - VARHDRSZ);
}


/*****************************************************************************
 * 用于 bpchar 的比较函数
 *
 * 注意：btree 索引需要这些例程不泄漏内存；因此，
 * 小心释放加压数据的工作副本。大多数地方无需如此小心。
 *****************************************************************************/

static void fc_check_collation_set(Oid fc_collid)
{
	if (!OidIsValid(fc_collid))
	{
		/*
		 * 这通常意味着解析器无法解决隐式排序的冲突，
		 * 因此以这种方式报告。
		 */
		ereport(ERROR,
				(errcode(ERRCODE_INDETERMINATE_COLLATION),
				 errmsg("could not determine which collation to use for string comparison"),
				 errhint("Use the COLLATE clause to set the collation explicitly.")));
	}
}

Datum bpchareq(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_len1,
				fc_len2;
	bool		fc_result;
	Oid			fc_collid = PG_GET_COLLATION();
	bool		fc_locale_is_c = false;
	pg_locale_t fc_mylocale = 0;

	fc_check_collation_set(fc_collid);

	fc_len1 = fc_bcTruelen(fc_arg1);
	fc_len2 = fc_bcTruelen(fc_arg2);

	if (lc_collate_is_c(fc_collid))
		fc_locale_is_c = true;
	else
		fc_mylocale = pg_newlocale_from_collation(fc_collid);

	if (fc_locale_is_c || !fc_mylocale || fc_mylocale->deterministic)
	{
		/* 由于我们只关心相等还是不相等，因此我们可以避免所有
		 * strcoll() 的开销，只需进行位比较即可。 */
		if (fc_len1 != fc_len2)
			fc_result = false;
		else
			fc_result = (memcmp(VARDATA_ANY(fc_arg1), VARDATA_ANY(fc_arg2), fc_len1) == 0);
	}
	else
	{
		fc_result = (varstr_cmp(VARDATA_ANY(fc_arg1), fc_len1, VARDATA_ANY(fc_arg2), fc_len2,
							 fc_collid) == 0);
	}

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_BOOL(fc_result);
}

Datum bpcharne(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_len1,
				fc_len2;
	bool		fc_result;
	Oid			fc_collid = PG_GET_COLLATION();
	bool		fc_locale_is_c = false;
	pg_locale_t fc_mylocale = 0;

	fc_check_collation_set(fc_collid);

	fc_len1 = fc_bcTruelen(fc_arg1);
	fc_len2 = fc_bcTruelen(fc_arg2);

	if (lc_collate_is_c(fc_collid))
		fc_locale_is_c = true;
	else
		fc_mylocale = pg_newlocale_from_collation(fc_collid);

	if (fc_locale_is_c || !fc_mylocale || fc_mylocale->deterministic)
	{
		/* 由于我们只关心相等还是不相等，因此我们可以避免所有
		 * strcoll() 的开销，只需进行位比较即可。 */
		if (fc_len1 != fc_len2)
			fc_result = true;
		else
			fc_result = (memcmp(VARDATA_ANY(fc_arg1), VARDATA_ANY(fc_arg2), fc_len1) != 0);
	}
	else
	{
		fc_result = (varstr_cmp(VARDATA_ANY(fc_arg1), fc_len1, VARDATA_ANY(fc_arg2), fc_len2,
							 fc_collid) != 0);
	}

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_BOOL(fc_result);
}

Datum bpcharlt(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_len1,
				fc_len2;
	int			fc_cmp;

	fc_len1 = fc_bcTruelen(fc_arg1);
	fc_len2 = fc_bcTruelen(fc_arg2);

	fc_cmp = varstr_cmp(VARDATA_ANY(fc_arg1), fc_len1, VARDATA_ANY(fc_arg2), fc_len2,
					 PG_GET_COLLATION());

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_BOOL(fc_cmp < 0);
}

Datum bpcharle(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_len1,
				fc_len2;
	int			fc_cmp;

	fc_len1 = fc_bcTruelen(fc_arg1);
	fc_len2 = fc_bcTruelen(fc_arg2);

	fc_cmp = varstr_cmp(VARDATA_ANY(fc_arg1), fc_len1, VARDATA_ANY(fc_arg2), fc_len2,
					 PG_GET_COLLATION());

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_BOOL(fc_cmp <= 0);
}

Datum bpchargt(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_len1,
				fc_len2;
	int			fc_cmp;

	fc_len1 = fc_bcTruelen(fc_arg1);
	fc_len2 = fc_bcTruelen(fc_arg2);

	fc_cmp = varstr_cmp(VARDATA_ANY(fc_arg1), fc_len1, VARDATA_ANY(fc_arg2), fc_len2,
					 PG_GET_COLLATION());

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_BOOL(fc_cmp > 0);
}

Datum bpcharge(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_len1,
				fc_len2;
	int			fc_cmp;

	fc_len1 = fc_bcTruelen(fc_arg1);
	fc_len2 = fc_bcTruelen(fc_arg2);

	fc_cmp = varstr_cmp(VARDATA_ANY(fc_arg1), fc_len1, VARDATA_ANY(fc_arg2), fc_len2,
					 PG_GET_COLLATION());

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_BOOL(fc_cmp >= 0);
}

Datum bpcharcmp(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_len1,
				fc_len2;
	int			fc_cmp;

	fc_len1 = fc_bcTruelen(fc_arg1);
	fc_len2 = fc_bcTruelen(fc_arg2);

	fc_cmp = varstr_cmp(VARDATA_ANY(fc_arg1), fc_len1, VARDATA_ANY(fc_arg2), fc_len2,
					 PG_GET_COLLATION());

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_INT32(fc_cmp);
}

Datum bpchar_sortsupport(PG_FUNCTION_ARGS)
{
	SortSupport fc_ssup = (SortSupport) PG_GETARG_POINTER(0);
	Oid			fc_collid = fc_ssup->ssup_collation;
	MemoryContext fc_oldcontext;

	fc_oldcontext = MemoryContextSwitchTo(fc_ssup->ssup_cxt);

	/* 使用通用字符串 SortSupport */
	varstr_sortsupport(fc_ssup, BPCHAROID, fc_collid);

	MemoryContextSwitchTo(fc_oldcontext);

	PG_RETURN_VOID();
}

Datum bpchar_larger(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_len1,
				fc_len2;
	int			fc_cmp;

	fc_len1 = fc_bcTruelen(fc_arg1);
	fc_len2 = fc_bcTruelen(fc_arg2);

	fc_cmp = varstr_cmp(VARDATA_ANY(fc_arg1), fc_len1, VARDATA_ANY(fc_arg2), fc_len2,
					 PG_GET_COLLATION());

	PG_RETURN_BPCHAR_P((fc_cmp >= 0) ? fc_arg1 : fc_arg2);
}

Datum bpchar_smaller(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_len1,
				fc_len2;
	int			fc_cmp;

	fc_len1 = fc_bcTruelen(fc_arg1);
	fc_len2 = fc_bcTruelen(fc_arg2);

	fc_cmp = varstr_cmp(VARDATA_ANY(fc_arg1), fc_len1, VARDATA_ANY(fc_arg2), fc_len2,
					 PG_GET_COLLATION());

	PG_RETURN_BPCHAR_P((fc_cmp <= 0) ? fc_arg1 : fc_arg2);
}


/* bpchar 需要专门的哈希函数，因为我们想在比较中忽略
 * 尾随空格。 */
Datum hashbpchar(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_key = PG_GETARG_BPCHAR_PP(0);
	Oid			fc_collid = PG_GET_COLLATION();
	char	   *fc_keydata;
	int			fc_keylen;
	pg_locale_t fc_mylocale = 0;
	Datum		fc_result;

	if (!fc_collid)
		ereport(ERROR,
				(errcode(ERRCODE_INDETERMINATE_COLLATION),
				 errmsg("could not determine which collation to use for string hashing"),
				 errhint("Use the COLLATE clause to set the collation explicitly.")));

	fc_keydata = VARDATA_ANY(fc_key);
	fc_keylen = fc_bcTruelen(fc_key);

	if (!lc_collate_is_c(fc_collid))
		fc_mylocale = pg_newlocale_from_collation(fc_collid);

	if (!fc_mylocale || fc_mylocale->deterministic)
	{
		fc_result = hash_any((unsigned char *) fc_keydata, fc_keylen);
	}
	else
	{
#ifdef USE_ICU
		if (fc_mylocale->provider == COLLPROVIDER_ICU)
		{
			int32_t		fc_ulen = -1;
			UChar	   *fc_uchar = NULL;
			Size		fc_bsize;
			uint8_t    *fc_buf;

			fc_ulen = icu_to_uchar(&fc_uchar, fc_keydata, fc_keylen);

			fc_bsize = ucol_getSortKey(fc_mylocale->info.icu.ucol,
									fc_uchar, fc_ulen, NULL, 0);
			fc_buf = palloc(fc_bsize);
			ucol_getSortKey(fc_mylocale->info.icu.ucol,
							fc_uchar, fc_ulen, fc_buf, fc_bsize);
			pfree(fc_uchar);

			fc_result = hash_any(fc_buf, fc_bsize);

			pfree(fc_buf);
		}
		else
#endif
			/* 不应该发生 */
			elog(ERROR, "unsupported collprovider: %c", fc_mylocale->provider);
	}

	/* 避免对烤制输入泄露内存 */
	PG_FREE_IF_COPY(fc_key, 0);

	return fc_result;
}

Datum hashbpcharextended(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_key = PG_GETARG_BPCHAR_PP(0);
	Oid			fc_collid = PG_GET_COLLATION();
	char	   *fc_keydata;
	int			fc_keylen;
	pg_locale_t fc_mylocale = 0;
	Datum		fc_result;

	if (!fc_collid)
		ereport(ERROR,
				(errcode(ERRCODE_INDETERMINATE_COLLATION),
				 errmsg("could not determine which collation to use for string hashing"),
				 errhint("Use the COLLATE clause to set the collation explicitly.")));

	fc_keydata = VARDATA_ANY(fc_key);
	fc_keylen = fc_bcTruelen(fc_key);

	if (!lc_collate_is_c(fc_collid))
		fc_mylocale = pg_newlocale_from_collation(fc_collid);

	if (!fc_mylocale || fc_mylocale->deterministic)
	{
		fc_result = hash_any_extended((unsigned char *) fc_keydata, fc_keylen,
								   PG_GETARG_INT64(1));
	}
	else
	{
#ifdef USE_ICU
		if (fc_mylocale->provider == COLLPROVIDER_ICU)
		{
			int32_t		fc_ulen = -1;
			UChar	   *fc_uchar = NULL;
			Size		fc_bsize;
			uint8_t    *fc_buf;

			fc_ulen = icu_to_uchar(&fc_uchar, VARDATA_ANY(fc_key), VARSIZE_ANY_EXHDR(fc_key));

			fc_bsize = ucol_getSortKey(fc_mylocale->info.icu.ucol,
									fc_uchar, fc_ulen, NULL, 0);
			fc_buf = palloc(fc_bsize);
			ucol_getSortKey(fc_mylocale->info.icu.ucol,
							fc_uchar, fc_ulen, fc_buf, fc_bsize);
			pfree(fc_uchar);

			fc_result = hash_any_extended(fc_buf, fc_bsize, PG_GETARG_INT64(1));

			pfree(fc_buf);
		}
		else
#endif
			/* 不应该发生 */
			elog(ERROR, "unsupported collprovider: %c", fc_mylocale->provider);
	}

	PG_FREE_IF_COPY(fc_key, 0);

	return fc_result;
}

/* 以下运算符支持 bpchar 数据项的逐字符比较，
 * 以允许构建适用于 LIKE 子句的索引。
 * 注意，常规的 bpchareq/bpcharne 比较运算符，以及
 * 常规支持函数 1 和 2 使用 "C" 排序被认为与这些兼容！
 */

static int fc_internal_bpchar_pattern_compare(BpChar *fc_arg1, BpChar *fc_arg2)
{
	int			fc_result;
	int			fc_len1,
				fc_len2;

	fc_len1 = fc_bcTruelen(fc_arg1);
	fc_len2 = fc_bcTruelen(fc_arg2);

	fc_result = memcmp(VARDATA_ANY(fc_arg1), VARDATA_ANY(fc_arg2), Min(fc_len1, fc_len2));
	if (fc_result != 0)
		return fc_result;
	else if (fc_len1 < fc_len2)
		return -1;
	else if (fc_len1 > fc_len2)
		return 1;
	else
		return 0;
}


Datum bpchar_pattern_lt(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_result;

	fc_result = fc_internal_bpchar_pattern_compare(fc_arg1, fc_arg2);

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_BOOL(fc_result < 0);
}


Datum bpchar_pattern_le(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_result;

	fc_result = fc_internal_bpchar_pattern_compare(fc_arg1, fc_arg2);

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_BOOL(fc_result <= 0);
}


Datum bpchar_pattern_ge(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_result;

	fc_result = fc_internal_bpchar_pattern_compare(fc_arg1, fc_arg2);

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_BOOL(fc_result >= 0);
}


Datum bpchar_pattern_gt(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_result;

	fc_result = fc_internal_bpchar_pattern_compare(fc_arg1, fc_arg2);

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_BOOL(fc_result > 0);
}


Datum btbpchar_pattern_cmp(PG_FUNCTION_ARGS)
{
	BpChar	   *fc_arg1 = PG_GETARG_BPCHAR_PP(0);
	BpChar	   *fc_arg2 = PG_GETARG_BPCHAR_PP(1);
	int			fc_result;

	fc_result = fc_internal_bpchar_pattern_compare(fc_arg1, fc_arg2);

	PG_FREE_IF_COPY(fc_arg1, 0);
	PG_FREE_IF_COPY(fc_arg2, 1);

	PG_RETURN_INT32(fc_result);
}


Datum btbpchar_pattern_sortsupport(PG_FUNCTION_ARGS)
{
	SortSupport fc_ssup = (SortSupport) PG_GETARG_POINTER(0);
	MemoryContext fc_oldcontext;

	fc_oldcontext = MemoryContextSwitchTo(fc_ssup->ssup_cxt);

	/* 使用通用字符串 SortSupport，强制使用 "C" 排序 */
	varstr_sortsupport(fc_ssup, BPCHAROID, C_COLLATION_OID);

	MemoryContextSwitchTo(fc_oldcontext);

	PG_RETURN_VOID();
}
